@file:Suppress("UnstableApiUsage")
@file:OptIn(ExperimentalSerializationApi::class)

package com.lalameow.researchstation.util.serialization

import com.lalameow.researchstation.api.technology.ItemRequirement
import com.lalameow.researchstation.block.RequirementStorage
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext
import net.silkmc.silk.nbt.serialization.serializer.CompoundTagSerializer

@Suppress("UnstableApiUsage")
class ItemRequirementStorageSerializer(private val context: TransactionContext) :
    KSerializer<RequirementStorage<ItemVariant, ItemRequirement.Progressing>> {
    private val serializer = ItemRequirement.Progressing.serializer()
    override val descriptor =
        buildClassSerialDescriptor("RequirementStorage<ItemVariant, ItemRequirement.Progressing>") {
            element<ItemRequirement.Progressing>("requirement")
            element("item", ItemVariantSerializer.descriptor)
        }

    override fun deserialize(decoder: Decoder) = decoder.beginStructure(descriptor).let {
        RequirementStorage(it.decodeSerializableElement(serializer.descriptor, 0, serializer), ItemVariant.blank())
            .apply {
                insert(
                    it.decodeSerializableElement(ItemVariantSerializer.descriptor, 1, ItemVariantSerializer),
                    capacity,
                    context
                )
            }
    }

    override fun serialize(encoder: Encoder, value: RequirementStorage<ItemVariant, ItemRequirement.Progressing>) {
        encoder.beginStructure(descriptor).also {
            it.encodeSerializableElement(
                serializer.descriptor,
                0,
                serializer,
                value.requirement
            )
            it.encodeSerializableElement(ItemVariantSerializer.descriptor, 1, ItemVariantSerializer, value.variant)
        }
    }
}

@OptIn(ExperimentalSerializationApi::class)
object ItemVariantSerializer : KSerializer<ItemVariant> {
    override val descriptor = SerialDescriptor("ItemVariant", CompoundTagSerializer.descriptor)

    override fun deserialize(decoder: Decoder) =
        ItemVariant.fromNbt(decoder.decodeSerializableValue(CompoundTagSerializer))!!

    override fun serialize(encoder: Encoder, value: ItemVariant) {
        encoder.encodeSerializableValue(CompoundTagSerializer, value.toNbt())
    }
}

@OptIn(ExperimentalSerializationApi::class)
object FluidVariantSerializer : KSerializer<FluidVariant> {
    override val descriptor = SerialDescriptor("FluidVariant", CompoundTagSerializer.descriptor)

    override fun deserialize(decoder: Decoder) =
        FluidVariant.fromNbt(decoder.decodeSerializableValue(CompoundTagSerializer))!!

    override fun serialize(encoder: Encoder, value: FluidVariant) {
        encoder.encodeSerializableValue(CompoundTagSerializer, value.toNbt())
    }
}
